package org.asiainfo.flowable.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.asiainfo.common.core.constant.ProcessConstants;
import org.asiainfo.common.core.enums.FlowComment;
import org.asiainfo.common.core.enums.ProcessStatus;
import org.asiainfo.common.core.exception.ServiceException;
import org.asiainfo.common.core.utils.SpringUtils;
import org.asiainfo.common.core.utils.StreamUtils;
import org.asiainfo.common.core.utils.StringUtils;
import org.asiainfo.common.flowable.factory.FlowServiceFactory;
import org.asiainfo.common.flowable.flow.CustomProcessDiagramGenerator;
import org.asiainfo.common.flowable.utils.FlowableUtils;
import org.asiainfo.common.flowable.utils.ModelUtils;
import org.asiainfo.common.satoken.utils.LoginHelper;
import org.asiainfo.flowable.domain.bo.WfTaskBo;
import org.asiainfo.flowable.service.WfCopyService;
import org.asiainfo.flowable.service.WfTaskService;
import org.asiainfo.flowable.utils.WfTaskUtils;
import org.asiainfo.system.api.RemoteUserService;
import org.flowable.bpmn.constants.BpmnXMLConstants;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.EndEvent;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.UserTask;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.common.engine.api.FlowableObjectNotFoundException;
import org.flowable.common.engine.impl.identity.Authentication;
import org.flowable.engine.ProcessEngineConfiguration;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.image.ProcessDiagramGenerator;
import org.flowable.task.api.DelegationState;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.InputStream;
import java.util.*;

/**
 * 工作流任务管理服务实现类
 *
 * @author dotor-ww
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class WfTaskServiceImpl extends FlowServiceFactory implements WfTaskService {

    @DubboReference
    private RemoteUserService remoteUserService;

    /**
     * 审核任务
     *
     * @param wfTaskBo 流程任务业务对象
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void completeTask(WfTaskBo wfTaskBo) {
        Task task = taskService.createTaskQuery().taskId(wfTaskBo.getTaskId()).singleResult();
        if (ObjectUtil.isNull(task)) {
            throw new ServiceException("任务不存在");
        }
        // 当前用户ID
        String userId = WfTaskUtils.getUserId();
        // 1.获取Bpmn模型
        BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());
        identityService.setAuthenticatedUserId(userId);

        // 2.判断任务是否处于委托状态
        if (DelegationState.PENDING.equals(task.getDelegationState())) {
            // 3.添加评论，解决任务，并设置所有者（owner）为assignee
            taskService.addComment(wfTaskBo.getTaskId(), wfTaskBo.getProcInsId(), FlowComment.DELEGATE.getType(), wfTaskBo.getComment());
            taskService.resolveTask(wfTaskBo.getTaskId());
        } else {
            taskService.addComment(wfTaskBo.getTaskId(), wfTaskBo.getProcInsId(), FlowComment.NORMAL.getType(), wfTaskBo.getComment());
            taskService.setAssignee(wfTaskBo.getTaskId(), userId);
            // 4.判断流程变量信息是否为空
            if (MapUtil.isNotEmpty(wfTaskBo.getVariables())) {
                // 获取模型信息
                String localScopeValue = ModelUtils
                    .getUserTaskAttributeValue(bpmnModel, task.getTaskDefinitionKey(), ProcessConstants.PROCESS_FORM_LOCAL_SCOPE);

                boolean localScope = Convert.toBool(localScopeValue, false);
                taskService.complete(wfTaskBo.getTaskId(), wfTaskBo.getVariables(), localScope);
            } else {
                taskService.complete(wfTaskBo.getTaskId());
            }
        }
        // 5.设置任务节点
        wfTaskBo.setTaskName(task.getName());
        // 6.判断是否需要设置下一节点处理人
        if (StringUtils.isNotBlank(wfTaskBo.getNextUserIds())) {
            assignNextUsers(bpmnModel, wfTaskBo.getProcInsId(), wfTaskBo.getNextUserIds());
        }
        // 7.判断是否需要抄送用户
        if (!SpringUtils.getBean(WfCopyService.class).makeCopy(wfTaskBo)) {
            throw new ServiceException("抄送失败");
        }
    }

    /**
     * 驳回任务
     *
     * @param wfTaskBo 流程任务业务对象
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void rejectTask(WfTaskBo wfTaskBo) {
        // 1.获取当前任务
        Task task = taskService.createTaskQuery().taskId(wfTaskBo.getTaskId()).singleResult();
        if (ObjectUtil.isNull(task)) {
            throw new ServiceException("获取当前任务失败");
        }
        if (task.isSuspended()) {
            throw new ServiceException("当前任务已挂起，无法驳回");
        }
        // 2.获取实例流程
        ProcessInstance processInstance = runtimeService
            .createProcessInstanceQuery()
            .processInstanceId(wfTaskBo.getProcInsId())
            .singleResult();

        if (ObjectUtil.isNull(processInstance)) {
            throw new ServiceException("流程实例不存在");
        }
        // 3.获取流程定义
        ProcessDefinition processDefinition = repositoryService
            .createProcessDefinitionQuery()
            .processDefinitionId(task.getProcessDefinitionId())
            .singleResult();

        String userId = WfTaskUtils.getUserId();
        // 4.设置当前用户为流程实例的发起人
        identityService.setAuthenticatedUserId(userId);
        // 5.添加驳回意见
        taskService.addComment(wfTaskBo.getTaskId(), wfTaskBo.getProcInsId(), FlowComment.REJECT.getType(), wfTaskBo.getComment());
        // 6.设置流程状态为终结
        runtimeService.setVariable(processInstance.getId(), ProcessConstants.PROCESS_STATUS_KEY, ProcessStatus.TERMINATED.getStatus());
        // 7.获取所有节点信息
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId());
        EndEvent endEvent = ModelUtils.getEndEvent(bpmnModel);
        // 8.终止流程
        List<Execution> executions = runtimeService.createExecutionQuery().parentId(task.getProcessInstanceId()).list();
        List<String> executionIds = StreamUtils.toList(executions, Execution::getId);

        runtimeService.createChangeActivityStateBuilder()
            .processInstanceId(task.getProcessInstanceId())
            .moveExecutionsToSingleActivityId(executionIds, endEvent.getId())
            .changeState();

        // 9.判断是否需要抄送用户
        if (!SpringUtils.getBean(WfCopyService.class).makeCopy(wfTaskBo)) {
            throw new ServiceException("抄送失败");
        }
    }

    /**
     * 退回任务
     *
     * @param wfTaskBo 流程任务业务对象
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void returnTask(WfTaskBo wfTaskBo) {
        Task task = taskService.createTaskQuery().taskId(wfTaskBo.getTaskId()).singleResult();
        if (ObjectUtil.isNull(task)) {
            throw new ServiceException("获取当前任务失败");
        }
        if (task.isSuspended()) {
            throw new ServiceException("当前任务已挂起，无法退回");
        }
        // 1.获取流程定义
        ProcessDefinition processDefinition = repositoryService
            .createProcessDefinitionQuery()
            .processDefinitionId(task.getProcessDefinitionId())
            .singleResult();

        // 2.获取流程模型
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId());
        // 3.获取当前节点元素
        FlowElement source = ModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey());
        // 4.获取跳转节点元素
        FlowElement target = ModelUtils.getFlowElementById(bpmnModel, wfTaskBo.getTargetKey());
        // 5.从当前节点向前扫描，判断当前节点与目标节点是否属于串行，若目标节点是在并行网关上或非同一路线上，不可跳转
        boolean isSequential = !ModelUtils.isSequentialReachable(source, target, new HashSet<>());
        if (isSequential) {
            throw new ServiceException("当前节点相对于目标节点，不属于串行关系，无法回退");
        }

        // 6.获取所有正常进行的任务节点 Key，这些任务不能直接使用，需要找出其中需要撤回的任务
        List<Task> runTasks = taskService
            .createTaskQuery()
            .processInstanceId(task.getProcessInstanceId())
            .list();

        List<String> runTaskKeyList = StreamUtils.toList(runTasks, Task::getTaskDefinitionKey);
        // 7.通过父级网关的出口连线，结合 runTaskList 比对，获取需要撤回的任务
        List<UserTask> currentUserTaskList = FlowableUtils.iteratorFindChildUserTasks(target, runTaskKeyList, null, null);
        // 8.需要退回的任务列表ID
        List<String> currentIds = StreamUtils.toList(currentUserTaskList, UserTask::getId);
        // 9.循环获取那些需要被撤回的节点的ID，用来设置驳回原因
        List<String> currentTaskIds = currentIds
            .stream()
            .flatMap(currentId -> runTasks
                .stream()
                .filter(runTask -> currentId.equals(runTask.getTaskDefinitionKey()))
                .map(Task::getId))
            .toList();

        // 设置处理人
        identityService.setAuthenticatedUserId(WfTaskUtils.getUserId());
        for (String currentTaskId : currentTaskIds) {
            // 10.设置退回原因
            taskService.addComment(currentTaskId, task.getProcessInstanceId(), FlowComment.REBACK.getType(), wfTaskBo.getComment());
        }

        try {
            // 11. 1对1或多对1情况，currentIds 当前要跳转的节点列表(1或多)，targetKey 跳转到的节点(1)
            runtimeService
                .createChangeActivityStateBuilder()
                .processInstanceId(task.getProcessInstanceId())
                .moveActivityIdsToSingleActivityId(currentIds, wfTaskBo.getTargetKey())
                .changeState();

        } catch (FlowableObjectNotFoundException e) {
            throw new ServiceException("未找到流程实例，流程可能已发生变化");
        } catch (FlowableException e) {
            throw new ServiceException("无法取消或开始活动");
        }

        // 12.设置任务节点名称
        wfTaskBo.setTaskName(task.getName());
        // 13.判断是否需要抄送用户
        if (!SpringUtils.getBean(WfCopyService.class).makeCopy(wfTaskBo)) {
            throw new ServiceException("抄送失败");
        }
    }

    /**
     * 删除任务
     *
     * @param wfTaskBo 流程任务业务对象
     */
    @Override
    public void deleteTask(WfTaskBo wfTaskBo) {
        // 设置处理人
        identityService.setAuthenticatedUserId(WfTaskUtils.getUserId());
        // 删除任务
        taskService.deleteTask(wfTaskBo.getTaskId(), wfTaskBo.getComment());
    }

    /**
     * 取消申请
     *
     * @param wfTaskBo 流程任务业务对象
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void stopProcess(WfTaskBo wfTaskBo) {
        List<Task> tasks = taskService
            .createTaskQuery()
            .processInstanceId(wfTaskBo.getProcInsId())
            .list();

        if (CollUtil.isEmpty(tasks)) {
            throw new ServiceException("未找到流程实例，流程可能已发生变化");
        }

        ProcessInstance processInstance = runtimeService
            .createProcessInstanceQuery()
            .processInstanceId(wfTaskBo.getProcInsId())
            .singleResult();

        BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId());

        if (ObjectUtil.isNotNull(bpmnModel)) {
            Process process = bpmnModel.getMainProcess();
            List<EndEvent> endNodes = process.findFlowElementsOfType(EndEvent.class, false);
            if (CollUtil.isNotEmpty(endNodes)) {
                Authentication.setAuthenticatedUserId(WfTaskUtils.getUserId());
                String endId = endNodes.get(0).getId();

                List<Execution> executions = runtimeService
                    .createExecutionQuery()
                    .parentId(processInstance.getProcessInstanceId())
                    .list();

                List<String> executionIds = StreamUtils.toList(executions, Execution::getId);

                // 变更流程为已结束状态
                runtimeService
                    .createChangeActivityStateBuilder()
                    .moveExecutionsToSingleActivityId(executionIds, endId)
                    .changeState();
            }
        }
    }

    /**
     * 获取流程任务变量
     *
     * @param taskId 流程任务ID
     * @return 流程任务变量
     */
    @Override
    public Map<String, Object> processVariables(String taskId) {
        HistoricTaskInstance historicTaskInstance = historyService
            .createHistoricTaskInstanceQuery()
            .includeProcessVariables()
            .finished()
            .taskId(taskId)
            .singleResult();

        if (ObjectUtil.isNotNull(historicTaskInstance)) {
            return historicTaskInstance.getProcessVariables();
        }

        return taskService.getVariables(taskId);
    }

    /**
     * 获取所有可回退节点
     *
     * @param wfTaskBo 流程任务业务对象
     * @return 回退节点
     */
    @Override
    public List<FlowElement> findReturnTaskList(WfTaskBo wfTaskBo) {
        Task task = taskService.createTaskQuery().taskId(wfTaskBo.getTaskId()).singleResult();
        // 1.获取流程定义
        ProcessDefinition processDefinition = repositoryService
            .createProcessDefinitionQuery()
            .processDefinitionId(task.getProcessDefinitionId())
            .singleResult();

        // 2.获取流程模型
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId());

        // 3.查询历史节点实例
        List<HistoricActivityInstance> activityInstanceList = historyService
            .createHistoricActivityInstanceQuery()
            .processInstanceId(task.getProcessInstanceId())
            .activityType(BpmnXMLConstants.ELEMENT_TASK_USER)
            .finished()
            .orderByHistoricActivityInstanceEndTime()
            .asc()
            .list();

        List<String> activityIds = activityInstanceList
            .stream()
            .map(HistoricActivityInstance::getActivityId)
            .filter(activityId -> !StringUtils.equals(activityId, task.getTaskDefinitionKey()))
            .distinct()
            .toList();

        // 4.获取当前任务节点元素
        FlowElement source = ModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey());
        List<FlowElement> elements = new ArrayList<>();
        for (String activityId : activityIds) {
            FlowElement target = ModelUtils.getFlowElementById(bpmnModel, activityId);
            boolean isSequential = ModelUtils.isSequentialReachable(source, target, new HashSet<>());
            if (isSequential) {
                elements.add(target);
            }
        }
        return elements;
    }

    /**
     * 认领、签收任务
     *
     * @param wfTaskBo 流程任务业务对象
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void claim(WfTaskBo wfTaskBo) {
        Task task = taskService.createTaskQuery().taskId(wfTaskBo.getTaskId()).singleResult();
        if (ObjectUtil.isNull(task)) {
            throw new ServiceException("当前任务不存在");
        }
        taskService.claim(wfTaskBo.getTaskId(), WfTaskUtils.getUserId());
    }

    /**
     * 取消认领、签收任务
     *
     * @param wfTaskBo 流程任务业务对象
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void unClaim(WfTaskBo wfTaskBo) {
        taskService.unclaim(wfTaskBo.getTaskId());
    }

    /**
     * 委派任务
     *
     * @param wfTaskBo 流程任务业务对象
     */
    @Override
    public void delegate(WfTaskBo wfTaskBo) {
        Task task = taskService.createTaskQuery().taskId(wfTaskBo.getTaskId()).singleResult();
        if (ObjectUtil.isNull(task)) {
            throw new ServiceException("当前任务不存在");
        }
        String nickName = remoteUserService.selectNickNameByUserId(LoginHelper.getUserId());

        String comment = LoginHelper.getNickName() + "->" + (StringUtils.isNotBlank(nickName) ? nickName : wfTaskBo.getUserId()) +
            (StringUtils.isNotBlank(wfTaskBo.getComment()) ? ": " + wfTaskBo.getComment() : "");

        String userId = WfTaskUtils.getUserId();
        // 1.设置处理人
        identityService.setAuthenticatedUserId(userId);
        // 2.添加审批意见
        taskService.addComment(wfTaskBo.getTaskId(), task.getProcessInstanceId(), FlowComment.DELEGATE.getType(), comment);
        // 3.设置办理人
        taskService.setOwner(wfTaskBo.getTaskId(), userId);
        // 4.执行委派
        taskService.delegateTask(wfTaskBo.getTaskId(), wfTaskBo.getUserId());
        // 5.设置任务节点名称
        wfTaskBo.setTaskName(task.getName());
        // 6.判断是否需要抄送用户
        if (!SpringUtils.getBean(WfCopyService.class).makeCopy(wfTaskBo)) {
            throw new ServiceException("抄送失败");
        }
    }

    /**
     * 转办任务
     *
     * @param wfTaskBo 流程任务业务对象
     */
    @Override
    public void transfer(WfTaskBo wfTaskBo) {
        Task task = taskService.createTaskQuery().taskId(wfTaskBo.getTaskId()).singleResult();

        if (ObjectUtil.isNull(task)) {
            throw new ServiceException("当前任务不存在");
        }
        String nickName = remoteUserService.selectNickNameByUserId(LoginHelper.getUserId());
        String comment = LoginHelper.getNickName() +
            "->" +
            (StringUtils.isNotBlank(nickName) ? nickName : wfTaskBo.getUserId()) +
            (StringUtils.isNotBlank(wfTaskBo.getComment()) ? ": " + wfTaskBo.getComment() : "");
        String userId = WfTaskUtils.getUserId();
        // 1.设置处理人
        identityService.setAuthenticatedUserId(userId);
        // 2.添加审批意见
        taskService.addComment(wfTaskBo.getTaskId(), task.getProcessInstanceId(), FlowComment.DELEGATE.getType(), comment);
        // 3.设置办理人
        taskService.setOwner(wfTaskBo.getTaskId(), userId);
        // 4.执行委派
        taskService.setAssignee(wfTaskBo.getTaskId(), wfTaskBo.getUserId());
        // 5.设置任务节点名称
        wfTaskBo.setTaskName(task.getName());
        // 6.判断是否需要抄送用户
        if (!SpringUtils.getBean(WfCopyService.class).makeCopy(wfTaskBo)) {
            throw new ServiceException("抄送失败");
        }
    }

    /**
     * 撤回流程
     *
     * @param wfTaskBo 流程任务业务对象
     */
    @Override
    public void revokeProcess(WfTaskBo wfTaskBo) {
        String procInsId = wfTaskBo.getProcInsId();
        String taskId = wfTaskBo.getTaskId();

        ProcessInstance processInstance = runtimeService
            .createProcessInstanceQuery()
            .processInstanceId(procInsId)
            .active()
            .singleResult();

        if (ObjectUtil.isNull(processInstance)) {
            throw new ServiceException("流程已结束或已挂起，无法执行撤回操作");
        }

        String userId = WfTaskUtils.getUserId();
        HistoricTaskInstance currentTaskIns = historyService
            .createHistoricTaskInstanceQuery()
            .taskId(taskId)
            .taskAssignee(userId)
            .singleResult();

        if (ObjectUtil.isNull(currentTaskIns)) {
            throw new ServiceException("当前任务不存在，无法执行撤回操作");
        }
        BpmnModel bpmnModel = repositoryService.getBpmnModel(currentTaskIns.getProcessDefinitionId());
        UserTask currentUserTask = ModelUtils.getUserTaskByKey(bpmnModel, currentTaskIns.getTaskDefinitionKey());

        List<UserTask> nextUserTasks = ModelUtils.findNextUserTasks(currentUserTask);
        List<String> nextUserTaskKeys = StreamUtils.toList(nextUserTasks, UserTask::getId);

        List<HistoricTaskInstance> finishedTaskInsList = historyService
            .createHistoricTaskInstanceQuery()
            .processInstanceId(procInsId)
            .taskCreatedAfter(currentTaskIns.getEndTime())
            .finished()
            .list();

        for (HistoricTaskInstance finishedTaskInstance : finishedTaskInsList) {
            if (CollUtil.contains(nextUserTaskKeys, finishedTaskInstance.getTaskDefinitionKey())) {
                throw new ServiceException("下一流程已处理,无法执行撤回操作");
            }
        }

        List<Task> activeTasks = taskService
            .createTaskQuery()
            .processInstanceId(procInsId)
            .list();

        identityService.setAuthenticatedUserId(userId);
        List<String> revokeExecutionIds = new ArrayList<>();
        for (Task task : activeTasks) {
            if (CollUtil.contains(nextUserTaskKeys, task.getTaskDefinitionKey())) {
                taskService.setAssignee(task.getId(), userId);
                String comment = LoginHelper.getNickName() + "撤回流程审批";
                taskService.addComment(task.getId(), task.getProcessInstanceId(), FlowComment.REVOKE.getType(), comment);
                revokeExecutionIds.add(task.getExecutionId());
            }
        }
        try {

            runtimeService
                .createChangeActivityStateBuilder()
                .processInstanceId(procInsId)
                .moveExecutionsToSingleActivityId(revokeExecutionIds, currentTaskIns.getTaskDefinitionKey())
                .changeState();

        } catch (FlowableObjectNotFoundException e) {
            throw new ServiceException("未找到流程实例，流程可能发生变化");
        } catch (FlowableException e) {
            throw new ServiceException("执行撤回流程失败");
        }
    }

    /**
     * 获取流程图
     *
     * @param processId 流程实例ID
     * @return 流程图
     */
    @Override
    public InputStream diagram(String processId) {

        // 1.获取当前的流程实例
        ProcessInstance processInstance = runtimeService
            .createProcessInstanceQuery()
            .processInstanceId(processId)
            .singleResult();

        String processDefinitionId = processInstance.getProcessDefinitionId();
        if (ObjectUtil.isNull(processInstance)) {

            HistoricProcessInstance pi = historyService
                .createHistoricProcessInstanceQuery()
                .processInstanceId(processId)
                .singleResult();

            processDefinitionId = pi.getProcessDefinitionId();
        }

        // 2.获得活动的节点
        List<HistoricActivityInstance> highLightedFlowList = historyService
            .createHistoricActivityInstanceQuery()
            .processInstanceId(processId)
            .orderByHistoricActivityInstanceStartTime()
            .asc()
            .list();

        List<String> highLightedFlows = new ArrayList<>();
        List<String> highLightedNodes = new ArrayList<>();
        //高亮线
        for (HistoricActivityInstance tempActivity : highLightedFlowList) {
            if ("sequenceFlow".equals(tempActivity.getActivityType())) {
                //高亮线
                highLightedFlows.add(tempActivity.getActivityId());
            } else {
                //高亮节点
                highLightedNodes.add(tempActivity.getActivityId());
            }
        }
        //获取流程图
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
        ProcessEngineConfiguration configuration = processEngine.getProcessEngineConfiguration();
        //获取自定义图片生成器
        ProcessDiagramGenerator diagramGenerator = new CustomProcessDiagramGenerator();
        return diagramGenerator.generateDiagram(bpmnModel, "png", highLightedNodes, highLightedFlows, configuration.getActivityFontName(),
            configuration.getLabelFontName(), configuration.getAnnotationFontName(), configuration.getClassLoader(), 1.0, true);
    }

    /**
     * 设置下一节点处理人
     *
     * @param bpmnModel    Bpmn模型
     * @param processInsId 流程实例ID
     * @param userIds      处理人
     */
    private void assignNextUsers(BpmnModel bpmnModel, String processInsId, String userIds) {
        List<Task> tasks = taskService.createTaskQuery().processInstanceId(processInsId).list();
        if (tasks.size() == 0) {
            return;
        }
        // 处理人
        Queue<String> assignIds = CollUtil.newLinkedList(userIds.split(","));
        if (assignIds.size() == tasks.size()) {
            for (Task task : tasks) {
                taskService.setAssignee(task.getId(), assignIds.poll());
            }
            return;
        }

        // 优先处理非多实例任务
        Iterator<Task> iterator = tasks.iterator();
        while (iterator.hasNext()) {
            Task task = iterator.next();
            if (!ModelUtils.isMultiInstance(bpmnModel, task.getTaskDefinitionKey())) {
                if (!assignIds.isEmpty()) {
                    taskService.setAssignee(task.getId(), assignIds.poll());
                }
                iterator.remove();
            }
        }

        // 若存在多实例任务，则进行动态加减签
        if (CollUtil.isNotEmpty(tasks)) {
            if (assignIds.isEmpty()) {
                // 动态减签
                for (Task task : tasks) {
                    runtimeService.deleteMultiInstanceExecution(task.getExecutionId(), true);
                }
            } else {
                // 动态加签
                for (String assignId : assignIds) {
                    Map<String, Object> assignVariables = Collections.singletonMap(BpmnXMLConstants.ATTRIBUTE_TASK_USER_ASSIGNEE, assignId);
                    runtimeService
                        .addMultiInstanceExecution(tasks.get(0).getTaskDefinitionKey(), tasks.get(0).getProcessInstanceId(), assignVariables);
                }
            }
        }
    }
}
